package com.dp.mallchat.common.chat.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import com.dp.mallchat.common.chat.dao.*;
import com.dp.mallchat.common.chat.domain.entity.*;
import com.dp.mallchat.common.chat.domain.enums.MessageMarkActTypeEnum;
import com.dp.mallchat.common.chat.domain.enums.MessageTypeEnum;
import com.dp.mallchat.common.chat.domain.vo.req.*;
import com.dp.mallchat.common.chat.domain.vo.req.member.MemberReq;
import com.dp.mallchat.common.chat.domain.vo.resp.ChatMemberStatisticResp;
import com.dp.mallchat.common.chat.domain.vo.resp.ChatMessageResp;
import com.dp.mallchat.common.chat.domain.vo.resp.MsgReadInfoResp;
import com.dp.mallchat.common.chat.domain.vo.resp.MsgReadPageResp;
import com.dp.mallchat.common.chat.service.ChatService;
import com.dp.mallchat.common.chat.service.ContactService;
import com.dp.mallchat.common.chat.service.adapter.ChatAdapter;
import com.dp.mallchat.common.chat.service.adapter.MemberAdapter;
import com.dp.mallchat.common.chat.service.adapter.MessageAdapter;
import com.dp.mallchat.common.chat.service.cache.RoomCache;
import com.dp.mallchat.common.chat.service.helper.ChatMemberHelper;
import com.dp.mallchat.common.chat.service.strategy.ReCallMsgHandler;
import com.dp.mallchat.common.chat.service.strategy.mark.AbstractMarkHandler;
import com.dp.mallchat.common.chat.service.strategy.mark.MarkFactory;
import com.dp.mallchat.common.common.annotation.MyRedissonLock;
import com.dp.mallchat.common.common.domain.vo.req.CursorPageBaseReq;
import com.dp.mallchat.common.common.domain.vo.resp.CursorPageBaseResp;
import com.dp.mallchat.common.common.utils.AssertUtil;
import com.dp.mallchat.common.user.dao.UserDao;
import com.dp.mallchat.common.user.domain.user.entity.User;
import com.dp.mallchat.common.user.domain.user.enums.RoleEnum;
import com.dp.mallchat.common.user.domain.ws.enums.ChatActiveStatusEnum;
import com.dp.mallchat.common.user.domain.ws.resp.ChatMemberResp;
import com.dp.mallchat.common.user.service.RoleService;
import com.dp.mallchat.common.user.service.cache.UserCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: dupeng
 * @CreateTime: 2024-07-05  18:36
 * @Description: 聊天实现类
 */
@Service
@Slf4j
public class ChatServiceImpl implements ChatService {

    @Autowired
    private MessageDao messageDao;
    @Autowired
    private RoomCache roomCache;
    @Autowired
    private ContactDao contactDao;
    @Autowired
    private MessageMarkDao messageMarkDao;
    @Autowired
    private RoleService roleService;
    @Autowired
    private ReCallMsgHandler reCallMsgHandler;
    @Autowired
    private ContactService contactService;
    @Autowired
    private UserDao userDao;
    @Autowired
    private RoomGroupDao roomGroupDao;
    @Autowired
    private GroupMemberDao groupMemberDao;
    @Autowired
    private UserCache userCache;

    /**
     * 获取推送消息响应对象
     *
     * @param message 消息
     * @param uid     uid
     * @return
     */
    @Override
    public ChatMessageResp getMsgResp(Message message, Long uid) {
        return CollUtil.getFirst(getMsgRespBatch(Collections.singletonList(message), uid));
    }

    /**
     * 送者获取消息内容，更快速响应
     *
     * @param msgId 消息id
     * @param uid   uid
     * @return
     */
    @Override
    public ChatMessageResp getMsgRespApi(Long msgId, Long uid) {
        Message message = messageDao.getById(msgId);
        return CollUtil.getFirst(getMsgRespBatch(Collections.singletonList(message), uid));
    }

    /**
     * 获取推送消息响应对象
     *
     * @param messageList 消息
     * @param uid         uid
     * @return
     */
    @Override
    public List<ChatMessageResp> getMsgRespBatch(List<Message> messageList, Long uid) {
        //MessageMark
        List<MessageMark> messageMark = messageMarkDao.getMessageMarkByMsgId(messageList.stream().map(Message::getId).collect(Collectors.toList()));
        return MessageAdapter.buildMsgResp(messageList, messageMark, uid);

    }

    @Override
    public CursorPageBaseResp<ChatMessageResp> getContactMsgPage(ContractMsgPageReq req, Long uid) {
        //用最后一条消息，限制被踢出去的人能看到的聊天记录
        Long lastMsgId = getLastMsgId(req.getRoomId(), uid);
        //游标读取消息
        CursorPageBaseResp<Message> messagePage = messageDao.getMessagePage(req.getRoomId(), req, lastMsgId);
        if (CollectionUtils.isEmpty(messagePage.getList())) {
            return CursorPageBaseResp.empty();
        }
        //返回消息体组装
        List<ChatMessageResp> msgRespBatch = getMsgRespBatch(messagePage.getList(), uid);
        return CursorPageBaseResp.init(messagePage, msgRespBatch);
    }

    /**
     * 撤回消息
     *
     * @param req 请求参数
     * @param uid uid
     */
    @Override
    public void recall(ReCallReq req, Long uid) {
        Message message = messageDao.getById(req.getMsgId());
        //校验消息能否撤回
        checkRecall(message, uid);
        //撤回消息
        reCallMsgHandler.recallMsg(message, uid);
    }


    /**
     * 获取消息的已读未读总数
     *
     * @param uid     uid
     * @param request 请求参数
     * @return
     */
    @Override
    public Collection<MsgReadInfoResp> getMsgReadInfo(Long uid, ChatMessageReadInfoReq request) {
        List<Message> messageList = messageDao.listByIds(request.getMsgIds());
        //限制自己发出的消息
        messageList.forEach(item -> {
            AssertUtil.equal(item.getFromUid(), uid, "只能查询自己的消息");
        });
        //获取已读未读数
        return contactService.getMsgReadInfo(messageList, uid);
    }


    @Override
    public void msgRead(Long uid, ChatMessageMemberReq request) {
        Contact contact = contactDao.getByRoomId(uid, request.getRoomId());
        if (Objects.isNull(contact)) {
            Contact save = new Contact();
            save.setRoomId(request.getRoomId());
            save.setUid(uid);
            save.setReadTime(new Date());
            contactDao.save(save);
        } else {
            Contact update = new Contact();
            update.setId(contact.getId());
            update.setReadTime(new Date());
            contactDao.updateById(update);
        }
    }


    /**
     * 消息点踩
     *
     * @param uid     uid
     * @param request 请求参数
     */
    @Override
    @MyRedissonLock(key = "#uid")
    public void msgMark(Long uid, ChatMessageMarkReq request) {
        AbstractMarkHandler strategy = MarkFactory.getStrategyOrNull(request.getMarkType());
        AssertUtil.isNotEmpty(strategy, "不支持该类型");
        //两个行为，打标，取消打标
        switch (MessageMarkActTypeEnum.of(request.getActType())) {
            case ACT:
                strategy.mark(uid, request.getMsgId());
                break;
            case NO_ACT:
                strategy.unMark(uid, request.getMsgId());
                break;
        }
    }

    /**
     * 消息已读未读列表
     *
     * @param uid     uid
     * @param request 请求参数
     * @return
     */
    @Override
    public CursorPageBaseResp<MsgReadPageResp> msgReadPage(Long uid, MsgReadPageReq request) {
        Long msgId = request.getMsgId();
        Message message = messageDao.getById(msgId);
        AssertUtil.equal(message.getFromUid(), uid, "只能查询自己的消息");

        CursorPageBaseResp<Contact> page;
        if (Objects.equals(1, request.getSearchType())) {
            //已读
            page = contactDao.getReadUid(message, request);
        } else {
            //未读
            page = contactDao.getUnReadUid(message, request);
        }

        if (CollectionUtils.isEmpty(page.getList())) {
            return CursorPageBaseResp.empty();
        }
        return CursorPageBaseResp.init(page, ChatAdapter.buildReadResp(page.getList()));
    }

    @Override
    public CursorPageBaseResp<ChatMemberResp> getMemberPage(List<Long> memberUidList, MemberReq request) {
        Pair<ChatActiveStatusEnum, String> pair = ChatMemberHelper.getCursorPair(request.getCursor());
        ChatActiveStatusEnum activeStatusEnum = pair.getKey();
        String timeCursor = pair.getValue();
        List<ChatMemberResp> resultList = new ArrayList<>();//最终列表
        Boolean isLast = Boolean.FALSE;
        if (activeStatusEnum == ChatActiveStatusEnum.ONLINE) {//在线列表
            CursorPageBaseResp<User> cursorPage = userDao.getCursorPage(memberUidList, new CursorPageBaseReq(request.getPageSize(), timeCursor), ChatActiveStatusEnum.ONLINE);
            resultList.addAll(MemberAdapter.buildMember(cursorPage.getList()));//添加在线列表
            if (cursorPage.getIsLast()) {//如果是最后一页,从离线列表再补点数据
                activeStatusEnum = ChatActiveStatusEnum.OFFLINE;
                Integer leftSize = request.getPageSize() - cursorPage.getList().size();
                cursorPage = userDao.getCursorPage(memberUidList, new CursorPageBaseReq(leftSize, null), ChatActiveStatusEnum.OFFLINE);
                resultList.addAll(MemberAdapter.buildMember(cursorPage.getList()));//添加离线线列表
            }
            timeCursor = cursorPage.getCursor();
            isLast = cursorPage.getIsLast();
        } else if (activeStatusEnum == ChatActiveStatusEnum.OFFLINE) {//离线列表
            CursorPageBaseResp<User> cursorPage = userDao.getCursorPage(memberUidList, new CursorPageBaseReq(request.getPageSize(), timeCursor), ChatActiveStatusEnum.OFFLINE);
            resultList.addAll(MemberAdapter.buildMember(cursorPage.getList()));//添加离线线列表
            timeCursor = cursorPage.getCursor();
            isLast = cursorPage.getIsLast();
        }
        // 获取群成员角色ID
        List<Long> uidList = resultList.stream().map(ChatMemberResp::getUid).collect(Collectors.toList());
        RoomGroup roomGroup = roomGroupDao.getByRoomId(request.getRoomId());
        Map<Long, Integer> uidMapRole = groupMemberDao.getMemberMapRole(roomGroup.getId(), uidList);
        resultList.forEach(member -> member.setRoleId(uidMapRole.get(member.getUid())));
        //组装结果
        return new CursorPageBaseResp<>(ChatMemberHelper.generateCursor(activeStatusEnum, timeCursor), isLast, resultList);
    }

    @Override
    public ChatMemberStatisticResp getMemberStatistic() {
        System.out.println(Thread.currentThread().getName());
        Long onlineNum = userCache.getOnlineNum();
//        Long offlineNum = userCache.getOfflineNum();不展示总人数
        ChatMemberStatisticResp resp = new ChatMemberStatisticResp();
        resp.setOnlineNum(onlineNum);
//        resp.setTotalNum(onlineNum + offlineNum);
        return resp;
    }


    private void checkRecall(Message message, Long uid) {
        AssertUtil.isNotEmpty(message, "消息不存在");
        AssertUtil.notEqual(MessageTypeEnum.RECALL.getType(), message.getType(), "消息无法撤回");
        //超管放行
        if (roleService.hasPower(uid, RoleEnum.ADMIN)) {
            return;
        }

        AssertUtil.equal(message.getFromUid(), uid, "只能撤回自己的消息");
        Date createTime = message.getCreateTime();
        long between = DateUtil.between(createTime, new Date(), DateUnit.MINUTE);
        AssertUtil.isTrue(between < 2, "只能撤回二分钟内的消息");
    }

    private Long getLastMsgId(Long roomId, Long uid) {
        Room room = roomCache.get(roomId);
        AssertUtil.isNotEmpty(room, "房间不存在");
        if (room.isHotRoom()) {
            return null;
        }
        AssertUtil.isNotEmpty(uid, "请登录后查看");
        return contactDao.getLastMsgIdBy(roomId, uid).getLastMsgId();
    }
}
